<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="description" content="迎着朝阳的博客" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    2、Spring 资源 |  迎着朝阳
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="https://dxysun.com/static/yan.png" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  
<script>
var _hmt = _hmt || [];
(function() {
	var hm = document.createElement("script");
	hm.src = "https://hm.baidu.com/hm.js?aa994a8d65700b8835787dd39d079d7e";
	var s = document.getElementsByTagName("script")[0]; 
	s.parentNode.insertBefore(hm, s);
})();
</script>


</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-springForResources"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  2、Spring 资源
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/12/13/springForResources/" class="article-date">
  <time datetime="2020-12-13T08:20:08.000Z" itemprop="datePublished">2020-12-13</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/springdocs/">spring官方文档</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">5.5k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">20 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      



  
    <div class="article-entry" itemprop="articleBody">
       
  <p>本章介绍Spring如何处理资源以及如何在Spring中使用资源<br><a id="resources"></a></p>
<a id="more"></a>


<p><a href="#resources"></a>2. 资源</p>
<p><strong>——————–</strong></p>
<p>本章介绍Spring如何处理资源以及如何在Spring中使用资源。 它包括以下主题:</p>
<p>*   <a href="#resources-introduction">简介</a></p>
<p>*   <a href="#resources-resource">资源接口</a></p>
<p>*   <a href="#resources-implementations">内置资源实现</a></p>
<p>*   <a href="#resources-resourceloader"><code>ResourceLoader</code></a></p>
<p>*   <a href="#resources-resourceloaderaware"><code>ResourceLoaderAware</code> 接口</a></p>
<p>*   <a href="#resources-as-dependencies">资源依赖</a></p>
<p>*   <a href="#resources-app-ctx">应用上下文和资源路径</a></p>
<p><a id="resources-introduction"></a></p>
<p><strong>### <a href="#resources-introduction"></a>2.1. 简介</strong></p>
<p>遗憾的是，Java的标准<code>java.net.URL</code>类和各种 <code>URL</code>前缀的标准处理程序不足以完全访问底层资源。例如，没有标准化的 <code>URL</code>实现可用于访问需要从类路径或相对于 <code>ServletContext</code>获取的资源。 虽然可以为专用 <code>URL</code>前缀注册新的处理程序（类似于<code>http:</code> :)这样的前缀的现有处理程序，但这通常非常复杂，并且<code>URL</code>接口仍然缺少一些理想的功能，例如检查当前资源是否存在的方法。</p>
<p><a id="resources-resource"></a></p>
<p><strong>### <a href="#resources-resource"></a>2.2. 资源接口</strong></p>
<p>Spring的<code>Resource</code>接口的目标是成为一个更强大的接口，用于抽象对底层资源的访问。 以下清单显示了<code>Resource</code>接口定义：</p>
<p>```java</p>
<p>public interface Resource extends InputStreamSource {</p>
<p>​    boolean exists();</p>
<p>​    boolean isOpen();</p>
<p>​    URL getURL() throws IOException;</p>
<p>​    File getFile() throws IOException;</p>
<p>​    Resource createRelative(String relativePath) throws IOException;</p>
<p>​    String getFilename();</p>
<p>​    String getDescription();</p>
<p>}</p>
<p>```</p>
<p>正如<code>Resource</code>接口的定义所示，它扩展了<code>InputStreamSource</code>接口。 以下清单显示了<code>InputStreamSource</code>接口的定义：</p>
<p>```java</p>
<p>public interface InputStreamSource {</p>
<p>​    InputStream getInputStream() throws IOException;</p>
<p>}</p>
<p>```</p>
<p><code>Resource</code> 接口中一些最重要的方法是：</p>
<p>*   <code>getInputStream()</code>: 用于定位和打开当前资源, 返回当前资源的<code>InputStream</code> ，预计每一次调用都会返回一个新的<code>InputStream</code>. 因此调用者必须自行关闭当前的输出流.</p>
<p>*   <code>exists()</code>: 返回<code>boolean</code>值，表示当前资源是否存在。</p>
<p>*   <code>isOpen()</code>: 返回<code>boolean</code>值，表示当前资源是否有已打开的输入流。如果为 <code>true</code>，那么<code>InputStream</code>不能被多次读取 ，只能在一次读取后即关闭以防止内存泄漏。除了<code>InputStreamResource</code>外，其他常用Resource实现都会返回<code>false</code>。</p>
<p>*   <code>getDescription()</code>: 返回当前资源的描述，当处理资源出错时，资源的描述会用于输出错误的信息。一般来说，资源的描述是一个完全限定的文件名称，或者是当前资源的真实URL。</p>
<p>其他方法允许您获取表示资源的实际<code>URL</code>或<code>File</code>对象（如果底层实现兼容并支持该功能）。</p>
<p>在Spring里， <code>Resource</code>抽象有着相当广泛的使用，例如，当需要某个资源时， <code>Resource</code>可以当作方法签名里的参数类型被使用。在Spring API中，有些方法（例如各种<code>ApplicationContext</code>实现的构造函数） 会直接采用普通格式的<code>String</code>路径来创建合适的<code>Resource</code>，调用者也可以通过在路径里带上指定的前缀来创建特定的<code>Resource</code>实现。</p>
<p>不但Spring内部和使用Spring的应用都大量地使用了<code>Resource</code>接口,而且开发者在应用代码中将它作为一个通用的工具类也是非常通用的。当你仅需要使用到<code>Resource</code>接口实现时， 可以直接忽略Spring的其余部分.虽然这样会与Spring耦合,但是也只是耦合一部分而已。使用这些<code>Resource</code>实现代替底层的访问是极其美好的。这与开发者引入其他库的目的也是一样的</p>
<p><code>Resource</code>抽象不会取代功能。 它尽可能地包裹它。 例如，<code>UrlResource</code>包装<code>URL</code>并使用包装的URL来完成其工作。</p>
<p><a id="resources-implementations"></a></p>
<p><strong>### <a href="#resources-implementations"></a>2.3. 内置的资源实现</strong></p>
<p>Spring包括以下<code>Resource</code>实现:</p>
<p>*   <a href="#resources-implementations-urlresource"><code>UrlResource</code></a></p>
<p>*   <a href="#resources-implementations-classpathresource"><code>ClassPathResource</code></a></p>
<p>*   <a href="#resources-implementations-filesystemresource"><code>FileSystemResource</code></a></p>
<p>*   <a href="#resources-implementations-servletcontextresource"><code>ServletContextResource</code></a></p>
<p>*   <a href="#resources-implementations-inputstreamresource"><code>InputStreamResource</code></a></p>
<p>*   <a href="#resources-implementations-bytearrayresource"><code>ByteArrayResource</code></a></p>
<p><a id="resources-implementations-urlresource"></a></p>
<p><strong>#### <a href="#resources-implementations-urlresource"></a>2.3.1. <code>UrlResource</code></strong></p>
<p><code>UrlResource</code> 封装了<code>java.net.URL</code>用来访问正常URL的任意对象。例如<code>file:</code> ，HTTP目标，FTP目标等。所有的URL都可以用标准化的字符串来表示，例如通过正确的标准化前缀。 可以用来表示当前URL的类型。 这包括<code>file:</code>，用于访问文件系统路径，<code>http:</code> ：用于通过HTTP协议访问资源，<code>ftp:</code>：用于通过FTP访问资源，以及其他。</p>
<p>通过java代码可以显式地使用<code>UrlResource</code>构造函数来创建<code>UrlResource</code>，但也可以调用API方法来使用代表路径的String参数来隐式创建<code>UrlResource</code>。 对于后一种情况，JavaBeans <code>PropertyEditor</code>最终决定要创建哪种类型的<code>Resource</code>。如果路径字符串包含众所周知的（对于它，那么）前缀（例如 <code>classpath:</code>:)，它会为该前缀创建适当的专用<code>Resource</code>。 但是，如果它无法识别前缀，则假定该字符串是标准URL字符串并创建<code>UrlResource</code>。</p>
<p><a id="resources-implementations-classpathresource"></a></p>
<p><strong>#### <a href="#resources-implementations-classpathresource"></a>2.3.2. <code>ClassPathResource</code></strong></p>
<p>ClassPathResource代表从类路径中获取资源，它使用线程上下文加载器，指定类加载器或给定class类来加载资源。</p>
<p>当类路径上资源存于文件系统中时，<code>ClassPathResource</code>支持使用<code>java.io.File</code>来访问。但是当类路径上的资源位于未解压(没有被Servlet引擎或其他可解压的环境解压）的jar包中时， <code>ClassPathResource</code>就不再支持以<code>java.io.File</code>的形式访问。鉴于此，Spring中各种Resource的实现都支持以<code>java.net.URL</code>的形式访问资源。</p>
<p>可以显式使用<code>ClassPathResource</code>构造函数来创建<code>ClassPathResource</code>，但是更多情况下，是调用API方法使用的。即使用一个代表路径的<code>String</code>参数来隐式创建<code>ClassPathResource</code>。 对于后一种情况，将会由JavaBeans的<code>PropertyEditor</code>来识别路径中<code>classpath:</code>前缀，并创建<code>ClassPathResource</code>。</p>
<p><a id="resources-implementations-filesystemresource"></a></p>
<p><strong>#### <a href="#resources-implementations-filesystemresource"></a>2.3.3. <code>FileSystemResource</code></strong></p>
<p><code>FileSystemResource</code>是用于处理<code>java.io.File</code>和<code>java.nio.file.Path</code>的实现，显然，它同时能解析作为<code>File</code>和作为<code>URL</code>的资源。</p>
<p><a id="resources-implementations-servletcontextresource"></a></p>
<p><strong>#### <a href="#resources-implementations-servletcontextresource"></a>2.3.4. <code>ServletContextResource</code></strong></p>
<p>这是<code>ServletContext</code>资源的 <code>Resource</code>实现，用于解释相关Web应用程序根目录中的相对路径。</p>
<p>ServletContextResource完全支持以流和URL的方式访问资源，但只有当Web项目是解压的（不是以war等压缩包形式存在），而且该ServletContext资源必须位于文件系统中， 它支持以<code>java.io.File</code>的方式访问资源。无论它是在文件系统上扩展还是直接从JAR或其他地方（如数据库）（可以想象）访问，实际上都依赖于Servlet容器。</p>
<p><a id="resources-implementations-inputstreamresource"></a></p>
<p><strong>#### <a href="#resources-implementations-inputstreamresource"></a>2.3.5. <code>InputStreamResource</code></strong></p>
<p><code>InputStreamResource</code>是针对<code>InputStream</code>提供的<code>Resource</code>实现。在一般情况下，如果确实无法找到合适的<code>Resource</code>实现时，才去使用它。 同时请优先选择<code>ByteArrayResource</code>或其他基于文件的<code>Resource</code>实现，迫不得已的才使用它。</p>
<p>与其他<code>Resource</code> 实现相比，这是已打开资源的描述符。 因此，它从<code>isOpen()</code>返回<code>true</code>。</p>
<p><a id="resources-implementations-bytearrayresource"></a></p>
<p><strong>#### <a href="#resources-implementations-bytearrayresource"></a>2.3.6. <code>ByteArrayResource</code></strong></p>
<p>这是给定字节数组的<code>Resource</code>实现。 它为给定的字节数组创建一个<code>ByteArrayInputStream</code>。</p>
<p>当需要从字节数组加载内容时，ByteArrayResource会是个不错的选择，无需求助于单独使用的<code>InputStreamResource</code>。</p>
<p><a id="resources-resourceloade"></a></p>
<p><strong>### <a href="#resources-resourceloader"></a>2.4.<code>ResourceLoader</code></strong></p>
<p><code>ResourceLoader</code>接口用于加载<code>Resource</code>对象，换句话说，就是当一个对象需要获取<code>Resource</code>实例时，可以选择实现<code>ResourceLoader</code>接口，以下清单显示了<code>ResourceLoader</code>接口定义：。</p>
<p>```java</p>
<p>public interface ResourceLoader {</p>
<p>​    Resource getResource(String location);</p>
<p>}</p>
<p>```</p>
<p>所有应用程序上下文都实现<code>ResourceLoader</code>接口。 因此，可以使用所有应用程序上下文来获取 <code>Resource</code>实例。</p>
<p>当在特殊的应用上下文中调用<code>getResource()</code>方法以及指定的路径没有特殊前缀时，将返回适合该特定应用程序上下文的<code>Resource</code>类型。 例如，假设针对<code>ClassPathXmlApplicationContext</code> 实例执行了以下代码片段：</p>
<p>​    Resource template = ctx.getResource(“some/resource/path/myTemplate.txt”);</p>
<p>针对<code>ClassPathXmlApplicationContext</code>，该代码返回 <code>ClassPathResource</code>。如果对<code>FileSystemXmlApplicationContext</code> 实例执行相同的方法，它将返回<code>FileSystemResource</code>。 对于<code>WebApplicationContext</code>，它将返回<code>ServletContextResource</code>。 它同样会为每个上下文返回适当的对象。</p>
<p>因此，您可以以适合特定应用程序上下文的方式加载资源。</p>
<p>另一方面，您可以通过指定特殊的<code>classpath:</code>前缀来强制使用<code>ClassPathResource</code>，而不管应用程序上下文类型如何，如下例所示：</p>
<p>```java</p>
<p>Resource template = ctx.getResource(“classpath:some/resource/path/myTemplate.txt”);</p>
<p>```</p>
<p>同样，您可以通过指定任何标准<code>java.net.URL</code>前缀来强制使用<code>UrlResource</code> 。 以下对示例使用<code>file</code> 和 <code>http</code>前缀：</p>
<p>```java</p>
<p>Resource template = ctx.getResource(“file:///some/resource/path/myTemplate.txt”);</p>
<p>Resource template = ctx.getResource(“<a href="http://myhost.com/resource/path/myTemplate.txt&quot;" target="_blank" rel="noopener">http://myhost.com/resource/path/myTemplate.txt&quot;</a>);</p>
<p>```</p>
<p>下表总结了将：<code>String</code>对象转换为<code>Resource</code>对象的策略:</p>
<p>Table 10.资源字符串</p>
<p>| 前缀       | 示例                                                 | 解释                                                         |</p>
<p>| ———- | —————————————————- | ———————————————————— |</p>
<p>| classpath: | <code>classpath:com/myapp/config.xml</code>                     | 从类路径加载                                                 |</p>
<p>| file:      | <a href="file:///data/config.xml">file:///data/config.xml</a>   | 从文件系统加载为<code>URL</code>。 另请参见<a href="#resources-filesystemresource-caveats"><code>FileSystemResource</code> 警告。</a> |</p>
<p>| http:      | <a href="http://myserver/logo.png" target="_blank" rel="noopener">http://myserver/logo.png</a> | 作为<code>URL</code>加载。                                              |</p>
<p>| (none)     | <code>/data/config.xml</code>                                   | 取决于底层的<code>ApplicationContext</code>。                           |</p>
<p><a id="resources-resourceloaderaware"></a></p>
<p><strong>### <a href="#resources-resourceloaderaware"></a>2.5. <code>ResourceLoaderAware</code> 接口</strong></p>
<p><code>ResourceLoaderAware</code>是一个特殊的标识接口，用来提供<code>ResourceLoader</code>引用的对象。以下清单显示了<code>ResourceLoaderAware</code>接口的定义：</p>
<p>```java</p>
<p>public interface ResourceLoaderAware {</p>
<p>​    void setResourceLoader(ResourceLoader resourceLoader);</p>
<p>}</p>
<p>```</p>
<p>当类实现<code>ResourceLoaderAware</code>并部署到应用程序上下文（作为Spring管理的bean）时，它被应用程序上下文识别为<code>ResourceLoaderAware</code>。 然后，应用程序上下文调用<code>setResourceLoader(ResourceLoader)</code>，将其自身作为参数提供（请记住，Spring中的所有应用程序上下文都实现了<code>ResourceLoader</code>接口）。</p>
<p>由于<code>ApplicationContext</code>实现了<code>ResourceLoader</code>，因此bean还可以实现 <code>ApplicationContextAware</code> 接口并直接使用提供的应用程序上下文来加载资源。 但是，通常情况下，如果您需要，最好使用专用的<code>ResourceLoader</code>接口。 代码只能耦合到资源加载接口（可以被认为是实用程序接口），而不能耦合到整个Spring <code>ApplicationContext</code>接口。</p>
<p>从Spring 2.5开始，除了实现<code>ResourceLoaderAware</code>接口，还可以采取另外一种替代方案-依赖<code>ResourceLoader</code>的自动装配。 “传统”构造函数和byType <a href="#beans-factory-autowire">自动装配</a>模式都支持对<code>ResourceLoader</code>的装配。 前者是以构造参数的形式装配，后者作为setter方法的参数参与装配。如果为了获得更大的灵活性（包括属性注入的能力和多参方法），可以考虑使用基于注解的新型注入方式。 使用注解<code>@Autowired</code>标识<code>ResourceLoader</code>变量，便可将其注入到成员属性、构造参数或方法参数中。这些参数需要ResourceLoader类型。 有关更多信息，请参阅使用<a href="#beans-autowired-annotation">Using <code>@Autowired</code></a>。</p>
<p><a id="resources-as-dependencies"></a></p>
<p><strong>### <a href="#resources-as-dependencies"></a>2.6. 资源依赖</strong></p>
<p>如果bean本身要通过某种动态过程来确定和提供资源路径，那么bean使用<code>ResourceLoader</code>接口来加载资源就变得更有意义了。假如需要加载某种类型的模板，其中所需的特定资源取决于用户的角色 。如果资源是静态的，那么完全可以不使用<code>ResourceLoader</code>接口，只需让bean公开它需要的<code>Resource</code>属性，并按照预期注入属性即可。</p>
<p>是什么使得注入这些属性变得如此简单？是因为所有应用程序上下文注册和使用一个特殊的<code>PropertyEditor</code> JavaBean，它可以将 <code>String</code> paths转换为<code>Resource</code>对象。 因此，如果<code>myBean</code>有一个类型为<code>Resource</code>的模板属性，它可以用一个简单的字符串配置该资源。如下所示：</p>
<p>```xml</p>
<bean id="myBean" class="...">

<p>​    <property name="template" value="some/resource/path/myTemplate.txt"/></p>
</bean>

<p>```</p>
<p>请注意，资源路径没有前缀。 因此，因为应用程序上下文本身将用作<code>ResourceLoader</code>， 所以资源本身通过<code>ClassPathResource</code>，<code>FileSystemResource</code>或<code>ServletContextResource</code>加载，具体取决于上下文的确切类型。</p>
<p>如果需要强制使用特定的 <code>Resource</code>类型，则可以使用前缀。 以下两个示例显示如何强制<code>ClassPathResource</code>和<code>UrlResource</code>（后者用于访问文件系统文件）：</p>
<p>```xml</p>
<property name="template" value="classpath:some/resource/path/myTemplate.txt">



<property name="template" value="file:///some/resource/path/myTemplate.txt"/>

<p>```</p>
<p><a id="resources-app-ctx"></a></p>
<p><strong>### <a href="#resources-app-ctx"></a>2.7. 应用上下文和资源路径</strong></p>
<p>本节介绍如何使用资源创建应用程序上下文，包括使用XML的快捷方式，如何使用通配符以及其他详细信息。</p>
<p><a id="resources-app-ctx-construction"></a></p>
<p><strong>#### <a href="#resources-app-ctx-construction"></a>2.7.1. 构造应用上下文</strong></p>
<p>应用程序上下文构造函数（对于特定的应用程序上下文类型）通常将字符串或字符串数组作为资源的位置路径，例如构成上下文定义的XML文件。</p>
<p>当指定的位置路径没有带前缀时，那么从指定位置路径创建<code>Resource</code>类型（用于后续加载bean定义），具体取决于所使用应用上下文。 例如，请考虑以下示例，该示例创建<code>ClassPathXmlApplicationContext</code>：</p>
<p>```java</p>
<p>ApplicationContext ctx = new ClassPathXmlApplicationContext(“conf/appContext.xml”);</p>
<p>```</p>
<p>bean定义是从类路径加载的，因为使用了<code>ClassPathResource</code>。 但是，请考虑以下示例，该示例创建 <code>FileSystemXmlApplicationContext</code>：</p>
<p>```java</p>
<p>ApplicationContext ctx =</p>
<p>​    new FileSystemXmlApplicationContext(“conf/appContext.xml”);</p>
<p>```</p>
<p>现在，bean定义是从文件系统位置加载的（在这种情况下，相对于当前工作目录）。</p>
<p>若位置路径带有classpath前缀或URL前缀，会覆盖默认创建的用于加载bean定义的<code>Resource</code>类型。请考虑以下示例：</p>
<p>```java</p>
<p>ApplicationContext ctx =</p>
<p>​    new FileSystemXmlApplicationContext(“classpath:conf/appContext.xml”);</p>
<p>```</p>
<p>使用<code>FileSystemXmlApplicationContext</code>从类路径加载bean定义。 但是，它仍然是<code>FileSystemXmlApplicationContext</code>。 如果它随后用作<code>ResourceLoader</code>，则任何未加前缀的路径仍被视为文件系统路径。</p>
<p><a id="resources-app-ctx-classpathxml"></a></p>
<p><strong>##### <a href="#resources-app-ctx-classpathxml"></a>构造<code>ClassPathXmlApplicationContext</code>实例的快捷方式</strong></p>
<p><code>ClassPathXmlApplicationContext</code>提供了多个构造函数，以利于快捷创建<code>ClassPathXmlApplicationContext</code>的实例。基础的想法是， 使用只包含多个XML文件名（不带路径信息）的字符串数组和一个Class参数的构造器，所省略路径信息<code>ClassPathXmlApplicationContext</code>会从<code>Class</code>参数中获取。</p>
<p>请考虑以下目录布局:</p>
<p>com/</p>
<p>  foo/</p>
<p>​    services.xml</p>
<p>​    daos.xml</p>
<p>​    MessengerService.class</p>
<p>以下示例显示如何实例化由名为<code>services.xml</code>和<code>daos.xml</code>（位于类路径中）的文件中定义的bean组成的<code>ClassPathXmlApplicationContext</code>实例：</p>
<p>```java</p>
<p>ApplicationContext ctx = new ClassPathXmlApplicationContext(</p>
<p>​    new String[] {“services.xml”, “daos.xml”}, MessengerService.class);</p>
<p>```</p>
<p>有关各种构造函数的详细信息，请参阅<a href="https://docs.spring.io/spring-framework/docs/5.1.3.BUILD-SNAPSHOT/javadoc-api/org/springframework/jca/context/SpringContextResourceAdapter.html" target="_blank" rel="noopener"><code>ClassPathXmlApplicationContext</code></a>javadoc。</p>
<p><a id="resources-app-ctx-wildcards-in-resource-paths"></a></p>
<p><strong>#### <a href="#resources-app-ctx-wildcards-in-resource-paths"></a>2.7.2. 使用通配符构造应用上下文</strong></p>
<p>从前文可知，应用上下文构造器的资源路径可以是单一的路径（即一对一地映射到目标资源）。也可以使用高效的通配符。可以包含特殊的”classpath*:”前缀或ant风格的正则表达式（使用Spring的PathMatcher来匹配）。</p>
<p>通配符机制可用于组装应用程序的组件，应用程序里所有组件都可以在一个公用的位置路径发布自定义的上下文片段，那么最终的应用上下文可使用<code>classpath*:</code>。 在同一路径前缀（前面的公用路径）下创建，这时所有组件上下文的片段都会被自动装配。</p>
<p>请注意，此通配符特定于在应用程序上下文构造函数中使用资源路径（或直接使用 <code>PathMatcher</code>实用程序类层次结构时），并在构造时解析。 它与资源类型本身无关。 您不能使用<code>classpath*:</code>前缀来构造实际的<code>Resource</code>,，因为资源一次只指向一个资源。</p>
<p><a id="resources-app-ctx-ant-patterns-in-paths"></a></p>
<p><strong>##### <a href="#resources-app-ctx-ant-patterns-in-paths"></a>Ant风格模式</strong></p>
<p>路径位置可以包含Ant样式模式，如以下示例所示:</p>
<p>/WEB-INF/*-context.xml</p>
<p>com/mycompany/**/applicationContext.xml</p>
<p>file:C:/some/path/*-context.xml</p>
<p>classpath:com/mycompany/**/applicationContext.xml</p>
<p>当路径位置包含Ant样式模式时，解析程序遵循更复杂的过程来尝试解析通配符。解释器会先从位置路径里获取最靠前的不带通配符的路径片段， 并使用这个路径片段来创建一个<code>Resource</code>，并从中获取一个URL。 如果此URL不是<code>jar:</code>URL或特定于容器的变体（例如，在WebLogic中为<code>zip:</code>，在WebSphere中为<code>wsjar</code>，等等） 则从Resource里获取<code>java.io.File</code>对象，并通过其遍历文件系统。进而解决位置路径里通配符。 对于jar URL，解析器要么从中获取<code>java.net.JarURLConnection</code>， 要么手动解析jar URL，然后遍历jar文件的内容以解析通配符。</p>
<p><a id="resources-app-ctx-portability"></a></p>
<p><strong>###### <a href="#resources-app-ctx-portability"></a>可移植性所带来的影响</strong></p>
<p>如果指定的路径定为文件URL（不管是显式还是隐式的），首先默认的<code>ResourceLoader</code>就是文件系统，其次通配符使用程序可以完美移植。</p>
<p>如果指定的路径是类路径位置，则解析器必须通过 <code>Classloader.getResource()</code>方法调用获取最后一个非通配符路径段URL。 因为这只是路径的一个节点（而不是末尾的文件），实际上它是未定义的（在<code>ClassLoader</code> javadoc中），在这种情况下并不能确定返回什么样的URL。 实际上，它始终会使用<code>java.io.File</code>来解析目录，其中类路径资源会解析到文件系统的位置或某种类型的jar URL，其中类路径资源解析为jar包的位置。 但是，这个操作就碰到了可移植的问题了。</p>
<p>如果获取了最后一个非通配符段的jar包URL，解析器必须能够从中获取<code>java.net.JarURLConnection</code>，或者手动解析jar包的URL，以便能够遍历jar的内容。 并解析通配符，这适用于大多数工作环境，但在某些其他特定环境中将会有问题，最后会导致解析失败，所以强烈建议在特定环境中彻底测试来自jar资源的通配符解析，测试成功之后再对其作依赖使用。</p>
<p><a id="resources-classpath-wildcards"></a></p>
<p><strong>##### <a href="#resources-classpath-wildcards"></a>The <code>classpath\*:</code> 前缀</strong></p>
<p>当构造基于XML文件的应用上下文时，位置路径可以使用<code>classpath*:</code>前缀。如以下示例所示：</p>
<p>```java</p>
<p>ApplicationContext ctx =</p>
<p>​    new ClassPathXmlApplicationContext(“classpath*:conf/appContext.xml”);</p>
<p>```</p>
<p><code>classpath*:</code>的使用表示该类路径下所有匹配文件名称的资源都会被获取（本质上就是调用了<code>ClassLoader.getResources(…)</code>方法，接着将获取到的资源装配成最终的应用上下文。</p>
<p>通配符类路径依赖于底层类加载器的<code>getResources()</code> 方法。由于现在大多数应用程序服务器都提供自己的类加载器实现，因此行为可能会有所不同，尤其是在处理jar文件时。 要在指定服务器测试<code>classpath*</code> 是否有效，简单点可以使用<code>getClass().getClassLoader().getResources(&quot;&lt;someFileInsideTheJar&gt;&quot;)</code>来加载类路径jar包里的文件。 尝试在两个不同的路径加载相同名称的文件，如果返回的结果不一致，就需要查看一下此服务器中与classloader设置相关的文档。</p>
<p>您还可以将<code>classpath*:</code> 前缀与位置路径的其余部分中的 <code>PathMatcher</code>模式组合在一起（例如，<code>classpath*:META-INF/*-beans.xml</code>）。 这种情况的解析策略非常简单，取位置路径最靠前的无通配符片段，然后调用<code>ClassLoader.getResources()</code>获取所有匹配到的类层次加载器加载资源，随后将<code>PathMatcher</code>的策略应用于每一个得到的资源。</p>
<p><a id="resources-wildcards-in-path-other-stuff"></a></p>
<p><strong>##### <a href="#resources-wildcards-in-path-other-stuff"></a>通配符的补充说明</strong></p>
<p>请注意，除非所有目标资源都存在文件系统中，否则<code>classpath*:</code>与Ant样式模式结合，都只能在至少有一个确定了根路径的情况下，才能达到预期的效果。 这意味着<code>classpath*:*.xml</code>等模式可能无法从jar文件的根目录中检索文件，而只能从根目录中的扩展目录中检索文件。</p>
<p>问题的根源是JDK的<code>ClassLoader.getResources()</code>方法的局限性。当向<code>ClassLoader.getResources()</code>传入空串时（表示搜索潜在的根目录）， 只能获取的文件系统的位置路径，即获取不了jar中文件的位置路径。Spring也会评估<code>URLClassLoader</code>运行时配置和jar文件中的<code>java.class.path</code>清单，但这不能保证导致可移植行为。</p>
<p>扫描类路径包需要在类路径中存在相应的目录条目。 使用Ant构建JAR时，请不要激活JAR任务的文件开关。 此外，在某些环境中，类路径目录可能不会基于安全策略公开 - 例如，JDK 1.7.0_45及更高版本上的独立应用程序（需要在清单中设置’Trusted-Library’ 。 请参阅<a href="https://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources" target="_blank" rel="noopener">http://stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources</a>)）。</p>
<p>在JDK 9的模块路径（Jigsaw）上，Spring的类路径扫描通常按预期工作。 此处强烈建议将资源放入专用目录，避免上述搜索jar文件根级别的可移植性问题。</p>
<p>如果有多个类路径上都用搜索到的根包，那么使用<code>classpath:</code>和ant风格模式一起指定资源并不保证会找到匹配的资源。请考虑以下资源位置示例：</p>
<p>com/mycompany/package1/service-context.xml</p>
<p>现在考虑一个人可能用来尝试查找该文件的Ant风格路径:</p>
<p>classpath:com/mycompany/**/service-context.xml</p>
<p>这样的资源可能只在一个位置，但是当使用前面例子之类的路径来尝试解析它时，解析器会处理<code>getResource(&quot;com/mycompany&quot;);</code>返回的（第一个）URL。 当在多个类路径存在基础包节点<code>&quot;com/mycompany&quot;</code>时(如在多个jar存在这个基础节点），解析器就不一定会找到指定资源。因此，这种情况下建议结合使用<code>classpath*:</code> 和ant风格模式，<code>classpath*:</code>会让解析器去搜索所有包含基础包节点的类路径。</p>
<p><a id="resources-filesystemresource-caveats"></a></p>
<p><strong>#### <a href="#resources-filesystemresource-caveats"></a>2.7.3. <code>FileSystemResource</code> 的警告</strong></p>
<p>当<code>FileSystemResource</code>与<code>FileSystemApplicationContext</code>之间没有联系（即，当<code>FileSystemApplicationContext</code>不是实际的<code>ResourceLoader</code>时）时会按预期处理绝对路径和相对路径。 相对路径是相对与当前工作目录而言的，而绝对路径则是相对文件系统的根目录而言的。</p>
<p>但是，出于向后兼容性（历史）的原因，当<code>FileSystemApplicationContext</code>是<code>ResourceLoader</code>时，这会发生变化。<code>FileSystemApplicationContext</code>强制所有有联系的<code>FileSystemResource</code>实例将所有位置路径视为相对路径， 无论它们是否以’/‘开头。 实际上，这意味着以下示例是等效的：</p>
<p>```java</p>
<p>ApplicationContext ctx =</p>
<p>​    new FileSystemXmlApplicationContext(“conf/context.xml”);</p>
<p>ApplicationContext ctx =</p>
<p>​    new FileSystemXmlApplicationContext(“/conf/context.xml”);</p>
<p>```</p>
<p>以下示例也是等效的（即使它们有所不同，因为一个案例是相对的而另一个案例是绝对的）：</p>
<p>```java</p>
<p>FileSystemXmlApplicationContext ctx = …;</p>
<p>ctx.getResource(“some/resource/path/myTemplate.txt”);</p>
<p>FileSystemXmlApplicationContext ctx = …;</p>
<p>ctx.getResource(“/some/resource/path/myTemplate.txt”);</p>
<p>```</p>
<p>实际上，如果确实需要使用绝对路径，建议放弃使用<code>FileSystemResource</code>和<code>FileSystemXmlApplicationContext</code>，而强制使用 <code>file:</code>的<code>UrlResource</code>。</p>
<p>```java</p>
<p>// actual context type doesn’t matter, the Resource will always be UrlResource</p>
<p>ctx.getResource(“file:///some/resource/path/myTemplate.txt”);</p>
<p>// force this FileSystemXmlApplicationContext to load its definition via a UrlResource</p>
<p>ApplicationContext ctx =</p>
<p>​    new FileSystemXmlApplicationContext(“file:///conf/context.xml”);</p>
<p>```</p>
 
      <!-- reward -->
      
      <div id="reword-out">
        <div id="reward-btn">
          打赏
        </div>
      </div>
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://dxysun.com/2020/12/13/springForResources/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/spring/" rel="tag">spring</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2020/12/13/springForvalidator/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            3、Spring 数据验证
          
        </div>
      </a>
    
    
      <a href="/2020/12/13/springForIocContainer/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">1、Spring IoC容器</div>
      </a>
    
  </nav>

  
   
  
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2024
        <i class="ri-heart-fill heart_icon"></i> dxysun
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
        <li>
          <a href="https://beian.miit.gov.cn" target="_black" rel="nofollow">豫ICP备17012675号-1</a>
        </li>
        
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="https://dxysun.com/static/logo.png" alt="迎着朝阳"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/photos">相册</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/alipay-20201219151322.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="https://tu.dxysun.com/weixin-20201219151346.png">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
      }
  });

  MathJax.Hub.Queue(function() {
      var all = MathJax.Hub.getAllJax(), i;
      for(i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
      }
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/mathjax@2.7.6/unpacked/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script>
  var ayerConfig = {
    mathjax: true
  }
</script>

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


<script src="/js/dz.js"></script>



    
  </div>
</body>

</html>